package jia.editor;

import jia.core.Compass;
import jia.core.GameStateManager;
import jia.core.Module;
import jia.core.Space;
import jia.core.Wall;
import jia.core.flags.WallFlags;
import jia.game.AbstractViewport;
import jia.game.ComponentUtils;

import javax.swing.*;
import javax.vecmath.Vector2d;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;

// ====================================================
/**
 * @author Alec Lanter
 */
public class ModuleEditorViewport extends AbstractViewport implements Scrollable{
//	private static final Logger log = Logger.getLogger(ModuleEditorViewport.class);
    protected int gridWidth = 32;
    protected int gridHeight = 32;
    protected static final Color COLOR_OBNOXIOUS_PINK = new Color(255, 32, 121);
    protected static final Stroke ROUNDED_STROKE = new BasicStroke(3, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER);
    protected static final Stroke LINE_STROKE = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER);
    protected static final String UNDEFINED_WALL = "__undefined__";
    protected Map<String, Color> wallColors;
    protected int lastMouseDragX, lastMouseDragY;
    
    // ====================================================
    /**
     */
    public ModuleEditorViewport(){
        setPreferredSize(new Dimension(300, 300));
        borderInsets = new Insets(0, 0, 0, 0);
        wallColors = new HashMap<String, Color>();
        wallColors.put(UNDEFINED_WALL, COLOR_OBNOXIOUS_PINK);
    }
    
    // ====================================================
    /**
     *
     */
    protected void renderView(Graphics2D g){
        // Go no further if we have nothing to draw.
        if(gameState == null) {
            return;
        }
        g.setStroke(ROUNDED_STROKE);
        Module module = gameState.getPlayerState().getCurrentModule();
        int modWidth = module.getWidth();
        int modHeight = module.getHeight();
        
        // Clear the screen
        g.setColor(ComponentUtils.getGlobalBackgroundColor());
        g.fillRect(0, 0, modWidth * gridWidth, modHeight * gridHeight);
        
        // Render out the area, one Space at a time
        int gridxPos, gridyPos;
        Space currentSpace;
        for(int x = 0; x < modWidth; x++) {
            gridxPos = gridWidth * x;
            for(int y = 0; y < modHeight; y++) {
                gridyPos = gridHeight * y;
                currentSpace = module.getSpace(x, y);
                if(currentSpace != null) {
                    // Render each wall
                    drawWall(currentSpace.getWall(Compass.NORTH), gridxPos, gridyPos, g);
                    drawWall(currentSpace.getWall(Compass.SOUTH), gridxPos, gridyPos, g);
                    drawWall(currentSpace.getWall(Compass.EAST), gridxPos, gridyPos, g);
                    drawWall(currentSpace.getWall(Compass.WEST), gridxPos, gridyPos, g);
                }
            }
        }
        
        // Draw an arrow at our location
        Vector2d playerPos = gameState.getPlayerState().getPosition();
        Compass playerFacing = gameState.getPlayerState().getFacing();
        if(playerPos != null) {
            int px = (int) playerPos.x * gridWidth + (gridWidth / 2);
            int py = (int) playerPos.y * gridHeight + (gridHeight / 2);
            g.setColor(Color.GREEN);
            g.setStroke(LINE_STROKE);
            switch(playerFacing.getDirectionalValue()) {
                case Compass.NORTH_VAL:
                    g.drawLine(px, py, px, py - 6);
                    g.drawLine(px, py - 6, px - 3, py - 3);
                    g.drawLine(px, py - 6, px + 3, py - 3);
                    break;
                case Compass.SOUTH_VAL:
                    g.drawLine(px, py, px, py + 6);
                    g.drawLine(px, py + 6, px - 3, py + 3);
                    g.drawLine(px, py + 6, px + 3, py + 3);
                    break;
                case Compass.EAST_VAL:
                    g.drawLine(px, py, px + 6, py);
                    g.drawLine(px + 6, py, px + 3, py - 3);
                    g.drawLine(px + 6, py, px + 3, py + 3);
                    break;
                case Compass.WEST_VAL:
                    g.drawLine(px, py, px - 6, py);
                    g.drawLine(px, py, px - 6, py);
                    g.drawLine(px - 6, py, px - 3, py - 3);
                    g.drawLine(px - 6, py, px - 3, py + 3);
            }
        }
        drawGrid(g);
    }
    
    // Render a line for a wall at the given graphics location
    private void drawWall(Wall wall, int x, int y, Graphics2D g){
        if(wall == null) {
            return;
        }
        Compass facing = wall.getFacing();
        int x2 = x;
        int y2 = y;
        switch(facing.getDirectionalValue()) {
            case Compass.SOUTH_VAL:
                y += gridHeight - 3;
                y2 += gridHeight - 3;
                x2 += gridWidth - 3;
                break;
            case Compass.NORTH_VAL:
                x2 += gridWidth - 3;
                break;
            case Compass.EAST_VAL:
                x += gridWidth - 3;
                x2 += gridWidth - 3;
                y2 += gridHeight - 3;
                break;
            case Compass.WEST_VAL:
                y2 += gridHeight - 3;
                break;
        }
        Color wallColor = wallColors.get(wall.getImageName());
        if(wallColor == null) {
            wallColor = wallColors.get(UNDEFINED_WALL);
        }
        g.setColor(wallColor);
        if(!wall.getFlags().hasFlag(WallFlags.OPEN)) {
            g.drawLine(x, y, x2, y2);
        } else {
            int x3 = x2, x4 = x;
            int y3 = y2, y4 = y;
            switch(facing.getDirectionalValue()) {
                case Compass.SOUTH_VAL:
                case Compass.NORTH_VAL:
                    x3 = x + (gridWidth / 4);
                    x4 = x2 - (gridWidth / 4);
                    break;
                case Compass.EAST_VAL:
                case Compass.WEST_VAL:
                    y3 = y + (gridHeight / 4);
                    y4 = y2 - (gridHeight / 4);
                    break;
            }
            g.drawLine(x, y, x3, y3);
            g.drawLine(x2, y2, x4, y4);
        }
    }
    
    // ====================================================
    /**
     * @return
     */
    public boolean getScrollableTracksViewportHeight(){
        return false;
    }
    
    // ====================================================
    /**
     * @return
     */
    public boolean getScrollableTracksViewportWidth(){
        return false;
    }
    
    // ====================================================
    /**
     * @return
     */
    public Dimension getPreferredScrollableViewportSize(){
        return getPreferredSize();
    }
    
    // ====================================================
    /**
     * @param viewableRect
     * @param orientation
     * @param direction
     *
     * @return
     */
    public int getScrollableBlockIncrement(Rectangle viewableRect, int orientation, int direction){
        // Scroll an even ten squares
        int pageSize;
        if(orientation == SwingConstants.VERTICAL) {
            pageSize = gridHeight * 10;
        } else {
            pageSize = gridWidth * 10;
        }
        return pageSize - getScrollableUnitIncrement(viewableRect, orientation, direction);
    }
    
    // ====================================================
    /**
     * @param viewableRect
     * @param orientation
     * @param direction
     *
     * @return
     */
    public int getScrollableUnitIncrement(Rectangle viewableRect, int orientation, int direction){
        int viewLeft = viewableRect.x;
        int viewTop = viewableRect.y;
        int offset = 0;
        
        // We don't need to do anything unless we're not on a grid space
        if((viewTop % gridHeight != 0) || (viewLeft % gridWidth != 0)) {
            // Crap. We've gotta work. ;)
            if(orientation == SwingConstants.VERTICAL) {
                if(direction < 0) {
                    offset = gridHeight - (viewTop % gridHeight);
                } else {
                    offset = viewTop % gridHeight;
                }
            } else {
                if(direction < 0) {
                    offset = gridWidth - (viewLeft % gridWidth);
                } else {
                    offset = viewLeft % gridWidth;
                }
            }
        }
        
        // We've calculated the offset, now determine which direction we're heading, and
        // return the appropriate value
        if(orientation == SwingConstants.VERTICAL) {
            return gridHeight - offset;
        } else {
            return gridWidth - offset;
        }
    }
    
    //====================================================
    /**
     *
     */
    public int getGridHeight(){
        return gridHeight;
    }
    
    //====================================================
    /**
     *
     */
    public void setGridHeight(int gridHeight){
        this.gridHeight = gridHeight;
        resize();
    }
    
    //====================================================
    /**
     *
     */
    public int getGridWidth(){
        return gridWidth;
    }
    
    //====================================================
    /**
     *
     */
    public void setGridWidth(int gridWidth){
        this.gridWidth = gridWidth;
        resize();
    }
    
    //====================================================
    /**
     */
    private void resize(){
        if(gameState != null) {
            Dimension d = getPreferredSize();
            d.height = gridHeight * gameState.getPlayerState().getCurrentModule().getHeight();
            d.width = gridWidth * gameState.getPlayerState().getCurrentModule().getWidth();
            setPreferredSize(d);
        }
    }
    
    //====================================================
    /**
     *
     */
    public void updateGameState(GameStateManager gameState){
        super.updateGameState(gameState);
        resize();
    }
    
    //====================================================
    /**
     * @param g
     */
    protected void drawGrid(Graphics2D g){
        g.setColor(Color.WHITE);
        int rectX;
        int rectY;
        for(int x = 0; x <= gameState.getPlayerState().getCurrentModule().getWidth(); x++) {
            rectX = (x * gridWidth) - 4;
            for(int y = 0; y <= gameState.getPlayerState().getCurrentModule().getHeight(); y++) {
                rectY = (y * gridHeight) - 4;
                g.fillRect(rectX, rectY, 6, 6);
            }
        }
    }
    
    //====================================================
    /**
     * @param wallImage
     * @param color
     */
    public void setWallImageColor(String wallImage, Color color){
        wallColors.put(wallImage, color);
    }
}
